
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "decl.h"

static long LINE = 1;
FILE *INPUT = nil;

Obj *pCons  = nil,
	*pCar   = nil,
	*pCdr   = nil,
    *pT     = nil,
    *pF     = nil,
    *pBegin = nil,
    *pCond  = nil,
    *pLambda= nil,
    *pNull  = nil,
    *pAtom  = nil,
    *pEq    = nil,
    *pDef   = nil,
    *Elist  = nil;

int 	
getChar () 		
{ 
	int c = fgetc (INPUT);
	if (c == '\n')
		LINE++;
	return c;
}

void	
ungetChar (int i)	
{ 
	if (i == '\n')
		LINE--;
	ungetc (i, INPUT);
}

int
isSpace (int c)
{
	if (c == ' ' || c == '\t' || c== '\v' || c == '\r' || c == '\n')
		return 1;
	return 0;
}

int
isDigit (int c)
{
	if ((c == '+' || c == '-') || (c >= '0' && c <= '9'))
		return 1;
	return 0;
}

int
isDelim (int c)
{
	char *s = SPECIALS;
	while (*s != '\0') {
		if (*s == c)
			return 1;
		s++;
	}
	return 0;
}

void
skipWs ()
{
	int c = getChar ();
	while (isSpace (c))
		c = getChar ();
	ungetChar (c);
}

void
skipComment ()
{
	int c = getChar ();
	while (c && c != '\n')
		c = getChar ();
}

Obj*
parseNumber (char *tok)
{
	long	l;
	float	f;
	char   *end;
	l = strtol (tok, &end, 0);
	if (*end == '\0')	/* is integer */
		return mkInt (l);
	f = strtof (tok, &end);
	if (*end == '\0')	/* is float */
		return mkFloat (f);
	return nil;
}

Obj*
parseSymbol (int i, char *tok)
{
	char 	*s;
	int		 k;

	{
        if (eqStr (tok, "t"))       return pT;
        if (eqStr (tok, "f"))       return pF;
		if (eqStr (tok, "cons")) 	return pCons;
		if (eqStr (tok, "car"))		return pCar;
		if (eqStr (tok, "cdr"))		return pCdr;
		if (eqStr (tok, "begin")) 	return pBegin;
		if (eqStr (tok, "cond"))	return pCond;
        if (eqStr (tok, "lambda"))  return pLambda;
        if (eqStr (tok, "isNull"))  return pNull;
        if (eqStr (tok, "isAtom"))  return pAtom;
        if (eqStr (tok, "eq"))      return pEq;
        if (eqStr (tok, "def"))     return pDef;
	}

	s = malloc (i);
	for (k = 0; k < i; k++)
		s[k] = tok[k];
	return mkSymbol (s);
}

Obj*
parseTok (int i, char *tok) 
{
	if (i) {
		if (isDigit (tok[0]))
			return parseNumber (tok);
		return parseSymbol (i, tok);
	}
	return parse ();
}

Obj*
getToken ()
{
	char tok[MAX_TOK];
	int	 i;
	for (i = 0; i < MAX_TOK + 1; i++) {
		tok[i] = getChar ();
		if (isDelim (tok[i])) {
			ungetChar (tok[i]);
			tok[i] = '\0';
			return parseTok (i, tok);
		}
	}
	return nil;
}

Obj*
parseList ()
{
	Obj *o = parse ();
	if (o == nil)
		return nil;
	return cons (o, parseList ());
}

Obj*
parse ()
{
	int c;

	skipWs ();
	c = getChar ();

	if (c == ';') {
		skipComment ();
		skipWs ();
		c = getChar ();
	}

	if (c == '(')	{ 
        Obj *o = parseList ();
        if (!o) return Elist;
        return o; 
    }

	if (c == ')')
        return nil;

	ungetChar (c);
	return getToken ();
}

Obj*
pObj (Obj *o)
{
	if (!o) {
		printf ("NIL");
		return nil;
	}

	switch (o->t) {
        case -1:        printf ("NIL"); break;
		case TInt: 		printf ("%ld",o->u.l); break;
		case TFloat:	printf ("%f",o->u.f);  break;
        case TBool:     printf ("%s",o->u.l ? "t" : "f"); break;
		case TSymbol:	printf ("%s",(char*)o->u.p);  break;
		case TList:	{
						Obj *x = o;
						printf ("(");
						do {
							pObj (car (x)); /*TODO::*/
							x = cdr (x);
							if (x) {
								printf (" ");
                                if (x->t != TList) {
                                    pObj (x);
                                    break;
                                }
                            }
						} while (x);
						printf (")");
						break;
					}
		default: printf ("<%d@%p>",o->t, o->u.p);
	}
	return o;
}

Obj*
apply (Obj *f, int l, Obj **args)
{
	Prim 	*p = f->u.p;
	int		 i;

	if (p->na != -1 && l != p->na) {
		printf ("ERROR: %d -> %d\n", l, p->na);
		return nil;
	}

	if (p->evArgs)
		for (i = 0; i < l; i++)
			args[i] = eval (args[i]);

	switch (p->na) {
		case 1:     return p->f (args[0]);
		case 2:     return p->f (args[0], args[1]);
		case 3:     return p->f (args[0], args[1], args[2]);
        case -1:    return p->f (l, args);
		default:    break;
	}
	return nil;
}

Obj*
eval (Obj *o)
{
	if (o) {
		if (o->t == TList) {
			int l, i;
			Obj *r = nil, **x = listToArray (o, &l);
            Obj *f = eval (x[0]);
            if (f->t == TPrimitive)
			    r = apply (f, l-1, x+1);
            else {
                if (f->t == TLambda) {
                    for (i = 1; i < l; i++)
                        x[i] = eval (x[i]);
                    r = applyLambda (f->u.p, l-1, x+1);
                }
            }
			free (x);
			return r;
		}
        if (o->t == TSymbol)
            return lookup (o->u.p);
	}
	return o;
}

int
main (int argc, char **argv)
{
	INPUT = fopen (argv[1], "r");
	if (!INPUT)
		return -1;


	pCons   = mkPrimitive (cons, 	2,  1);
	pCar    = mkPrimitive (car,	    1,  1);
	pCdr    = mkPrimitive (cdr,	    1,  1);
    pNull   = mkPrimitive (null,    1,  1);
    pAtom   = mkPrimitive (atom,    1,  1);
    pEq     = mkPrimitive (eq,      2,  1);
    pBegin  = mkPrimitive (begin,   -1, 0);
    pCond   = mkPrimitive (cond,    -1, 0);
    pLambda = mkPrimitive (lambda,  -1, 0);
    pDef    = mkPrimitive (def,     2,  0);

    Elist   = newObj (); Elist->t = -1;
    pT      = newObj (); pT->t=TBool; pT->u.l = 1;
    pF      = newObj (); pF->t=TBool; pF->u.l = 0;

	{
		Obj *o = parse ();
        while (o) {
		    o = eval (o);
            pObj (o);
            puts(" ");
            o = parse ();
        }
	}

	return 0;
}

	





